Skip to content

fix(moderation): add role hierarchy check to all mod actions#1228

Merged
kzndotsh merged 3 commits intomainfrom
1227-jail-does-not-respect-role-hierarchy
Mar 29, 2026
Merged

fix(moderation): add role hierarchy check to all mod actions#1228
kzndotsh merged 3 commits intomainfrom
1227-jail-does-not-respect-role-hierarchy

Conversation

@kzndotsh
Copy link
Copy Markdown
Contributor

@kzndotsh kzndotsh commented Mar 29, 2026

Closes #1227

Pull Request

Description

Provide a clear summary of your changes and reference any related issues. Include the motivation behind these changes and list any new dependencies if applicable.

If your PR is related to an issue, please include the issue number below:

Related Issue: Closes #

Type of Change:

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • Documentation update
  • Performance improvement
  • Code refactoring
  • Test improvements

Guidelines

  • My code follows the style guidelines of this project (formatted with Ruff)

  • I have performed a self-review of my own code

  • I have commented my code, particularly in hard-to-understand areas

  • I have made corresponding changes to the documentation if needed

  • My changes generate no new warnings

  • I have tested this change

  • Any dependent changes have been merged and published in downstream modules

  • I have added all appropriate labels to this PR

  • I have followed all of these guidelines.

How Has This Been Tested? (if applicable)

Please describe how you tested your code. e.g describe what commands you ran, what arguments, and any config stuff (if applicable)

Screenshots (if applicable)

Please add screenshots to help explain your changes.

Additional Information

Please add any other information that is important to this PR.

Summary by Sourcery

Bug Fixes:

  • Prevent non-owner moderators from executing moderation actions against members whose top role is equal to or higher than their own, aligning behavior with Discord role hierarchy.

@kzndotsh kzndotsh added this to the v0.2.0 milestone Mar 29, 2026
@kzndotsh kzndotsh self-assigned this Mar 29, 2026
@kzndotsh kzndotsh linked an issue Mar 29, 2026 that may be closed by this pull request
@kzndotsh kzndotsh added this to tux Mar 29, 2026
@github-project-automation github-project-automation bot moved this to Todo in tux Mar 29, 2026
@sourcery-ai
Copy link
Copy Markdown
Contributor

sourcery-ai bot commented Mar 29, 2026

Reviewer's guide (collapsed on small PRs)

Reviewer's Guide

Adds a Discord role hierarchy safety check to the generic moderation entrypoint so that all moderation actions respect role ordering before executing.

Sequence diagram for moderation action with role hierarchy check

sequenceDiagram
    actor Moderator
    participant DiscordGuild as DiscordGuild
    participant Bot as ModerationModule
    participant ModerationService

    Moderator->>Bot: moderate_user(ctx, user, case_type, **kwargs)
    Bot->>DiscordGuild: get ctx.guild, ctx.author, user.top_role
    alt User_is_guild_member_and_role_not_lower
        Bot-->>Moderator: _respond(ctx, cannot_moderate_higher_role)
        Note over Bot,Moderator: Early return, no moderation action executed
    else User_role_lower_than_moderator
        Bot->>ModerationService: execute_moderation_action(ctx, case_type, user, **kwargs)
        ModerationService-->>Moderator: Moderation result/confirmation
    end
Loading

Updated class diagram for moderation module role hierarchy check

classDiagram
    class ModerationModule {
        +moderate_user(ctx, user, case_type, kwargs)
        +_respond(ctx, message)
        -moderation : ModerationService
    }

    class ModerationService {
        +execute_moderation_action(ctx, case_type, user, kwargs)
    }

    class DiscordMember {
        +top_role : Role
    }

    class Context {
        +guild : Guild
        +author : DiscordMember
    }

    class Guild {
        +owner : DiscordMember
    }

    class Role {
    }

    ModerationModule --> ModerationService : uses
    ModerationModule --> Context : receives
    ModerationModule --> DiscordMember : moderates
    Context --> Guild : belongs_to
    DiscordMember --> Role : has
    Guild --> DiscordMember : owner
Loading

File-Level Changes

Change Details Files
Add a centralized role hierarchy guard to prevent moderating members with equal or higher roles than the command invoker.
  • Insert a pre-check in the generic moderate_user handler to run only when in a guild context and both target and author are guild members.
  • Exclude the guild owner from the restriction so they can always moderate others regardless of role order.
  • Compare the target member’s top_role against the invoker’s top_role and short‑circuit moderation when the target’s role is greater or equal.
  • Return an early user-facing response explaining why the action is blocked instead of proceeding to execute_moderation_action.
src/tux/modules/moderation/__init__.py

Assessment against linked issues

Issue Objective Addressed Explanation
#1227 Prevent the jail command from allowing a user to moderate (jail) another member whose highest role is equal to or higher than the command invoker's highest role, by enforcing a role hierarchy check.
#1227 Return an error response/message when attempting to jail (or otherwise moderate) a member with an equal or higher top role than the invoker, instead of performing the action.

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Mar 29, 2026

Warning

Rate limit exceeded

@kzndotsh has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 17 minutes and 55 seconds before requesting another review.

Your organization is not enrolled in usage-based pricing. Contact your admin to enable usage-based pricing to continue reviews beyond the rate limit, or try again in 17 minutes and 55 seconds.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 8fa696c6-4741-4b3f-9b8c-fa7030c19f02

📥 Commits

Reviewing files that changed from the base of the PR and between beb8e77 and 487f200.

📒 Files selected for processing (2)
  • CHANGELOG.md
  • src/tux/modules/moderation/__init__.py
📝 Walkthrough

Walkthrough

Added a role hierarchy validation in the moderate_user method that prevents moderation actions against users whose top role is equal to or higher than the executor's top role, excluding guild owners.

Changes

Cohort / File(s) Summary
Role Hierarchy Check
src/tux/modules/moderation/__init__.py
Added precondition in moderate_user to validate role hierarchy before executing moderation. Blocks action if target's top_role is greater than or equal to executor's top_role (excluding guild owner); responds with error and returns early.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The PR title clearly describes the main change: adding a role hierarchy check to moderation actions to prevent moderators from acting on users with equal or higher roles.
Linked Issues check ✅ Passed The code implements the required role hierarchy check by blocking moderation when target's top_role >= author's top_role, directly addressing issue #1227's requirement for preventing moderators from jailing senior moderators.
Out of Scope Changes check ✅ Passed The changes are narrowly focused on adding a role hierarchy precondition check to the moderate_user function, which is directly scoped to the linked issue requirements with no unrelated modifications.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Description check ✅ Passed The PR description references issue #1227 and includes a Sourcery summary explaining the role hierarchy check implementation for moderation actions.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch 1227-jail-does-not-respect-role-hierarchy
✨ Simplify code
  • Create PR with simplified code
  • Commit simplified code in branch 1227-jail-does-not-respect-role-hierarchy

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@amazon-q-developer amazon-q-developer bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review Summary

This PR adds role hierarchy checking to moderation actions, which addresses the reported issue. However, there's a critical logic gap that needs to be addressed before merge.

Critical Issue

The role hierarchy validation only checks if the moderator can perform the action, but doesn't verify if the bot itself has sufficient role permissions. This will cause moderation actions to fail silently after passing validation when the target user's role is equal to or higher than the bot's role.

Recommendation

Add an additional check to validate the bot's role hierarchy relative to the target user. This prevents confusing error states where the command appears to succeed but the Discord API rejects the action due to insufficient bot permissions.


You can now have the agent implement changes and create commits directly on your pull request's source branch. Simply comment with /q followed by your request in natural language to ask the agent to make changes.

Copy link
Copy Markdown
Contributor

@sourcery-ai sourcery-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey - I've left some high level feedback:

  • Consider extracting the role hierarchy check into a shared helper (e.g., can_moderate(ctx.author, user, ctx.guild)) so that any other moderation entry points can reuse the same logic and stay consistent if the rules need to evolve.
  • You may also want to mirror this hierarchy check against the bot’s own top role (ensuring the bot can actually act on the target) and reuse that in the same helper to keep user- and bot-side constraints in one place.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- Consider extracting the role hierarchy check into a shared helper (e.g., `can_moderate(ctx.author, user, ctx.guild)`) so that any other moderation entry points can reuse the same logic and stay consistent if the rules need to evolve.
- You may also want to mirror this hierarchy check against the bot’s own top role (ensuring the bot can actually act on the target) and reuse that in the same helper to keep user- and bot-side constraints in one place.

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Mar 29, 2026

📚 Documentation Preview

Type URL Version Message
Production https://tux.atl.dev - -
Preview https://cfc9c087-tux-docs.allthingslinux.workers.dev cfc9c087-4a0e-44f7-ba14-8399b8f17c68 Preview: tux@b2755102cb5d2f79782e7e93f2b9e8221d573f7b on 1228/merge by kzndotsh (run 503)

@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Mar 29, 2026

Dependency Review

✅ No vulnerabilities or license issues or OpenSSF Scorecard issues found.

Snapshot Warnings

⚠️: No snapshots were found for the head SHA 487f200.
Ensure that dependencies are being submitted on PR branches. Re-running this action after a short time may resolve the issue. See the documentation for more information and troubleshooting advice.

Scanned Files

None

@sentry
Copy link
Copy Markdown

sentry bot commented Mar 29, 2026

Codecov Report

❌ Patch coverage is 42.85714% with 4 lines in your changes missing coverage. Please review.
✅ Project coverage is 42.25%. Comparing base (8c4c480) to head (487f200).
⚠️ Report is 1 commits behind head on main.
✅ All tests successful. No failed tests found.

Files with missing lines Patch % Lines
src/tux/modules/moderation/__init__.py 42.85% 4 Missing ⚠️

❌ Your project check has failed because the head coverage (42.25%) is below the target coverage (80.00%). You can increase the head coverage or adjust the target coverage.

Additional details and impacted files
@@           Coverage Diff           @@
##             main    #1228   +/-   ##
=======================================
  Coverage   42.25%   42.25%           
=======================================
  Files         221      221           
  Lines       18402    18409    +7     
  Branches     2428     2431    +3     
=======================================
+ Hits         7775     7778    +3     
- Misses      10627    10631    +4     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

@kzndotsh kzndotsh moved this from Todo to In Progress in tux Mar 29, 2026
Copy link
Copy Markdown

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

1 issue found across 1 file

Confidence score: 3/5

  • There is a concrete regression risk in src/tux/modules/moderation/__init__.py: the hierarchy check covers the invoking moderator vs. target, but not the bot vs. target role position.
  • Because this is severity 6/10 with high confidence (9/10), moderation actions can still be attempted against members the bot cannot act on, which may cause failed commands or runtime permission errors for users.
  • This PR looks close, but this issue is user-facing enough to warrant a cautious merge score until the bot-role hierarchy validation is added.
  • Pay close attention to src/tux/modules/moderation/__init__.py - missing bot-vs-target role checks can break moderation actions on higher-role members.
Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="src/tux/modules/moderation/__init__.py">

<violation number="1" location="src/tux/modules/moderation/__init__.py:112">
P2: This hierarchy check validates the invoking moderator's role against the target but doesn't verify the **bot's** role position relative to the target. If the target member's top role is equal to or higher than the bot's top role, `execute_moderation_action` will proceed past this check but then fail with a `discord.Forbidden` at the API level, producing a confusing error. Add a second check for `ctx.guild.me.top_role <= user.top_role` and return a clear message like "I cannot moderate this member because their role is equal to or higher than mine."</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
src/tux/modules/moderation/__init__.py (1)

100-112: Consider protecting the guild owner as a moderation target.

The check correctly allows the guild owner to moderate anyone (ctx.author != ctx.guild.owner exempts them). However, it doesn't prevent moderators from attempting to moderate the guild owner. If the guild owner has a lower-positioned role than the moderator (unusual but possible), user.top_role >= ctx.author.top_role could be False, allowing the action to proceed—only to fail at the Discord API level.

Adding an explicit check for the target being the guild owner provides clearer UX and defensive coding:

♻️ Proposed enhancement
         # Role hierarchy check: only applies when the target is a guild member
         if (
             ctx.guild
             and isinstance(user, discord.Member)
             and isinstance(ctx.author, discord.Member)
+        ):
+            # Guild owner cannot be moderated by anyone
+            if user == ctx.guild.owner:
+                await self._respond(
+                    ctx,
+                    "You cannot moderate the server owner.",
+                )
+                return
+
+            # Non-owners cannot moderate members with equal or higher roles
+            if (
-            and ctx.author != ctx.guild.owner
-            and user.top_role >= ctx.author.top_role
-        ):
-            await self._respond(
-                ctx,
-                "You cannot moderate a member with an equal or higher role than yours.",
-            )
-            return
+                ctx.author != ctx.guild.owner
+                and user.top_role >= ctx.author.top_role
+            ):
+                await self._respond(
+                    ctx,
+                    "You cannot moderate a member with an equal or higher role than yours.",
+                )
+                return

Based on learnings: "Implement role-based permissions checks in Discord command modules"

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/tux/modules/moderation/__init__.py` around lines 100 - 112, Add an
explicit guard that prevents moderating the guild owner: before the existing
role-hierarchy block in the moderation command (where ctx, user and ctx.guild
are available and the current check is performed), check if user is the guild
owner (compare user == ctx.guild.owner or user.id == ctx.guild.owner_id) and
call self._respond with a clear message and return; keep this check above or
alongside the existing role comparison so attempts to moderate the guild owner
are rejected early and consistently.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@src/tux/modules/moderation/__init__.py`:
- Around line 100-112: Add an explicit guard that prevents moderating the guild
owner: before the existing role-hierarchy block in the moderation command (where
ctx, user and ctx.guild are available and the current check is performed), check
if user is the guild owner (compare user == ctx.guild.owner or user.id ==
ctx.guild.owner_id) and call self._respond with a clear message and return; keep
this check above or alongside the existing role comparison so attempts to
moderate the guild owner are rejected early and consistently.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 034811bd-7f1e-491e-bc76-05051828881f

📥 Commits

Reviewing files that changed from the base of the PR and between 0b20ce3 and beb8e77.

📒 Files selected for processing (1)
  • src/tux/modules/moderation/__init__.py
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
  • GitHub Check: Run All Tests (3.13.11)
  • GitHub Check: cubic · AI code reviewer
  • GitHub Check: Seer Code Review
  • GitHub Check: Dependencies
🧰 Additional context used
📓 Path-based instructions (2)
**/*.py

📄 CodeRabbit inference engine (.cursor/rules/rules.mdc)

**/*.py: Follow Python code style guide and best practices as defined in core/style-guide.mdc
Follow error handling patterns as defined in error-handling/patterns.mdc
Follow loguru logging patterns as defined in error-handling/logging.mdc
Follow Sentry integration patterns as defined in error-handling/sentry.mdc

**/*.py: Use strict type hints with Type | None syntax instead of Optional[Type] in Python files
Use NumPy docstrings in Python files
Prefer absolute imports; relative imports allowed only within the same module in Python files
Organize imports in Python files in the following order: stdlib → third-party → local, with blank lines between groups
Maintain 88 character line length in Python files
Use snake_case for functions and variables, PascalCase for classes, and UPPER_CASE for constants in Python
Always add imports to the top of Python files unless absolutely necessary
Keep individual Python files to a maximum of 1600 lines
Prefer one class or function per Python file when possible for improved maintainability
Use descriptive filenames with clear, purpose-driven names in Python files
Use custom exceptions for business logic errors with context and meaningful user messages
Never store secrets or sensitive credentials in code; use .env files and environment variables
Validate all user inputs at code boundaries in Python files
Type hints must be complete in pull requests
Docstrings must be provided for all public APIs in Python files

Files:

  • src/tux/modules/moderation/__init__.py
**/modules/**/*.py

📄 CodeRabbit inference engine (AGENTS.md)

**/modules/**/*.py: Use hybrid commands (slash + traditional) in Discord command modules
Implement role-based permissions checks in Discord command modules
Implement cooldowns and rate limiting for Discord commands
Use lazy loading to load modules and plugins on demand

Files:

  • src/tux/modules/moderation/__init__.py
🧠 Learnings (1)
📚 Learning: 2026-03-08T15:17:13.232Z
Learnt from: CR
Repo: allthingslinux/tux PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-03-08T15:17:13.232Z
Learning: Applies to **/modules/**/*.py : Implement role-based permissions checks in Discord command modules

Applied to files:

  • src/tux/modules/moderation/__init__.py
🔇 Additional comments (1)
src/tux/modules/moderation/__init__.py (1)

100-112: Role hierarchy check implementation looks correct.

The core logic properly addresses the issue from #1227:

  • Allows guild owners to bypass the role check (can moderate anyone)
  • Blocks moderation when target's top_role >= author's top_role
  • Only applies when both parties are guild members
  • Correctly placed before execute_moderation_action which has no internal role guards (confirmed by the coordinator snippet)

- Use owner_id instead of owner (avoids None if member uncached)
- Add bot-vs-target role check to prevent discord.Forbidden errors
@kzndotsh kzndotsh merged commit 79f9774 into main Mar 29, 2026
28 of 29 checks passed
@kzndotsh kzndotsh deleted the 1227-jail-does-not-respect-role-hierarchy branch March 29, 2026 12:29
@github-project-automation github-project-automation bot moved this from In Progress to Done in tux Mar 29, 2026
@kzndotsh kzndotsh removed this from tux Mar 29, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Jail does not respect role hierarchy

1 participant